#!/usr/bin/env python3
"""
Automatic Documentation Generation System for GSI/EnKF Fortran Code

This module provides automated documentation generation from structured comments
in Fortran source files. Supports multiple output formats including Sphinx,
Doxygen, HTML, and Markdown.

This is a standalone utility that uses the fortran_commentator package.

Author: GSI Development Team
Date: 2025-01-12
"""

import re
import os
import json
import sys
import argparse
import logging
from pathlib import Path
from typing import Dict, List, Optional, Union, Tuple, Any
from dataclasses import dataclass
from abc import ABC, abstractmethod
import subprocess

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


@dataclass
class Parameter:
    """Represents a function/subroutine parameter with direction and description."""
    name: str
    direction: str  # 'in', 'out', 'inout'
    description: str
    units: Optional[str] = None
    
    def __post_init__(self):
        """Extract units from description if present."""
        # Look for units in brackets or parentheses
        units_pattern = r'\[([^\]]+)\]|\(([^)]+)\)'
        match = re.search(units_pattern, self.description)
        if match and not self.units:
            self.units = match.group(1) or match.group(2)


@dataclass
class CodeElement:
    """Base class for documented code elements."""
    name: str
    brief: str
    details: str
    author: Optional[str] = None
    date: Optional[str] = None
    see_also: List[str] = None
    
    def __post_init__(self):
        """Initialize empty lists for mutable fields."""
        if self.see_also is None:
            self.see_also = []


@dataclass
class Function(CodeElement):
    """Represents a documented Fortran function."""
    parameters: List[Parameter] = None
    return_value: Optional[str] = None
    usage: Optional[str] = None
    algorithm: Optional[str] = None
    
    def __post_init__(self):
        """Initialize empty lists for mutable fields."""
        super().__post_init__()
        if self.parameters is None:
            self.parameters = []


@dataclass
class Subroutine(CodeElement):
    """Represents a documented Fortran subroutine."""
    parameters: List[Parameter] = None
    usage: Optional[str] = None
    algorithm: Optional[str] = None
    
    def __post_init__(self):
        """Initialize empty lists for mutable fields."""
        super().__post_init__()
        if self.parameters is None:
            self.parameters = []


@dataclass
class Module(CodeElement):
    """Represents a documented Fortran module."""
    components: List[str] = None
    functions: List[Function] = None
    subroutines: List[Subroutine] = None
    
    def __post_init__(self):
        """Initialize empty lists for mutable fields."""
        super().__post_init__()
        if self.components is None:
            self.components = []
        if self.functions is None:
            self.functions = []
        if self.subroutines is None:
            self.subroutines = []


@dataclass
class SourceFile:
    """Represents a documented Fortran source file."""
    filepath: Path
    modules: List[Module] = None
    functions: List[Function] = None
    subroutines: List[Subroutine] = None
    
    def __post_init__(self):
        """Initialize empty lists for mutable fields."""
        if self.modules is None:
            self.modules = []
        if self.functions is None:
            self.functions = []
        if self.subroutines is None:
            self.subroutines = []


class DocumentationGenerator(ABC):
    """Abstract base class for documentation generators."""
    
    @abstractmethod
    def generate_documentation(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate documentation from parsed source files."""
        pass
    
    @abstractmethod
    def generate_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate index/table of contents."""
        pass


class SphinxGenerator(DocumentationGenerator):
    """Generate Sphinx-compatible reStructuredText documentation."""
    
    def __init__(self, project_name: str = "GSI/EnKF Documentation"):
        """Initialize Sphinx generator."""
        self.project_name = project_name
    
    def generate_documentation(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate Sphinx documentation from source files."""
        logger.info("Generating Sphinx documentation...")
        
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Generate conf.py
        self._generate_conf_py(output_dir)
        
        # Generate main index.rst
        self._generate_main_index(source_files, output_dir)
        
        # Generate documentation for each source file
        for source_file in source_files:
            self._generate_file_documentation(source_file, output_dir)
        
        # Generate module index
        self._generate_module_index(source_files, output_dir)
        
        logger.info(f"Sphinx documentation generated in {output_dir}")
        return True
    
    def generate_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate Sphinx index files."""
        return self._generate_main_index(source_files, output_dir)
    
    def _generate_conf_py(self, output_dir: Path) -> None:
        """Generate Sphinx configuration file."""
        conf_content = f'''# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------

project = '{self.project_name}'
copyright = '2025, GSI Development Team'
author = 'GSI Development Team'

# The full version, including alpha/beta/rc tags
release = '1.0.0'

# -- General configuration ---------------------------------------------------

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon',
    'sphinx.ext.mathjax',
    'sphinx.ext.graphviz'
]

templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# -- Options for HTML output ------------------------------------------------

html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']

# -- Options for LaTeX output -----------------------------------------------

latex_elements = {{
    'papersize': 'letterpaper',
    'pointsize': '10pt',
    'preamble': r'\\usepackage{{amsmath}}\\usepackage{{amsfonts}}\\usepackage{{amssymb}}',
}}

# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = False
'''
        
        conf_path = output_dir / 'conf.py'
        with open(conf_path, 'w', encoding='utf-8') as f:
            f.write(conf_content)
    
    def _generate_main_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate main index.rst file."""
        content = f'''
{self.project_name}
{'=' * len(self.project_name)}

Welcome to the {self.project_name} documentation. This documentation is automatically 
generated from structured comments in Fortran source code.

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   modules/index
   api/index

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

'''
        
        index_path = output_dir / 'index.rst'
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        return True
    
    def _generate_file_documentation(self, source_file: SourceFile, output_dir: Path) -> None:
        """Generate documentation for a single source file."""
        # Create api directory
        api_dir = output_dir / 'api'
        api_dir.mkdir(exist_ok=True)
        
        # Generate filename for documentation
        filename = source_file.filepath.stem
        doc_path = api_dir / f'{filename}.rst'
        
        content = []
        content.append(f"{filename}")
        content.append("=" * len(filename))
        content.append("")
        content.append(f"Source file: ``{source_file.filepath}``")
        content.append("")
        
        # Document modules
        for module in source_file.modules:
            content.extend(self._format_module_rst(module))
        
        # Document functions
        for function in source_file.functions:
            content.extend(self._format_function_rst(function))
        
        # Document subroutines  
        for subroutine in source_file.subroutines:
            content.extend(self._format_subroutine_rst(subroutine))
        
        with open(doc_path, 'w', encoding='utf-8') as f:
            f.write('\n'.join(content))
    
    def _format_module_rst(self, module: Module) -> List[str]:
        """Format module documentation as reStructuredText."""
        content = []
        content.append(f"Module: {module.name}")
        content.append("-" * (8 + len(module.name)))
        content.append("")
        content.append(f"**Brief**: {module.brief}")
        content.append("")
        if module.details:
            content.append("**Details**")
            content.append("")
            content.append(module.details)
            content.append("")
        
        if module.components:
            content.append("**Components**")
            content.append("")
            for component in module.components:
                content.append(f"- {component}")
            content.append("")
        
        if module.see_also:
            content.append("**See Also**")
            content.append("")
            content.append(", ".join(module.see_also))
            content.append("")
        
        return content
    
    def _format_function_rst(self, function: Function) -> List[str]:
        """Format function documentation as reStructuredText."""
        content = []
        content.append(f"Function: {function.name}")
        content.append("-" * (10 + len(function.name)))
        content.append("")
        content.append(f"**Brief**: {function.brief}")
        content.append("")
        
        if function.details:
            content.append("**Details**")
            content.append("")
            content.append(function.details)
            content.append("")
        
        if function.parameters:
            content.append("**Parameters**")
            content.append("")
            for param in function.parameters:
                direction = param.direction.upper()
                units = f" [{param.units}]" if param.units else ""
                content.append(f"- ``{param.name}`` (*{direction}*): {param.description}{units}")
            content.append("")
        
        if function.return_value:
            content.append("**Returns**")
            content.append("")
            content.append(function.return_value)
            content.append("")
        
        if function.usage:
            content.append("**Usage**")
            content.append("")
            content.append("::")
            content.append("")
            for line in function.usage.split('\n'):
                content.append(f"    {line}")
            content.append("")
        
        if function.algorithm:
            content.append("**Algorithm**")
            content.append("")
            content.append(function.algorithm)
            content.append("")
        
        if function.see_also:
            content.append("**See Also**")
            content.append("")
            content.append(", ".join(function.see_also))
            content.append("")
        
        return content
    
    def _format_subroutine_rst(self, subroutine: Subroutine) -> List[str]:
        """Format subroutine documentation as reStructuredText."""
        content = []
        content.append(f"Subroutine: {subroutine.name}")
        content.append("-" * (12 + len(subroutine.name)))
        content.append("")
        content.append(f"**Brief**: {subroutine.brief}")
        content.append("")
        
        if subroutine.details:
            content.append("**Details**")
            content.append("")
            content.append(subroutine.details)
            content.append("")
        
        if subroutine.parameters:
            content.append("**Parameters**")
            content.append("")
            for param in subroutine.parameters:
                direction = param.direction.upper()
                units = f" [{param.units}]" if param.units else ""
                content.append(f"- ``{param.name}`` (*{direction}*): {param.description}{units}")
            content.append("")
        
        if subroutine.usage:
            content.append("**Usage**")
            content.append("")
            content.append("::")
            content.append("")
            for line in subroutine.usage.split('\n'):
                content.append(f"    {line}")
            content.append("")
        
        if subroutine.algorithm:
            content.append("**Algorithm**")
            content.append("")
            content.append(subroutine.algorithm)
            content.append("")
        
        if subroutine.see_also:
            content.append("**See Also**")
            content.append("")
            content.append(", ".join(subroutine.see_also))
            content.append("")
        
        return content
    
    def _generate_module_index(self, source_files: List[SourceFile], output_dir: Path) -> None:
        """Generate modules index file."""
        modules_dir = output_dir / 'modules'
        modules_dir.mkdir(exist_ok=True)
        
        content = []
        content.append("Modules")
        content.append("=======")
        content.append("")
        content.append(".. toctree::")
        content.append("   :maxdepth: 1")
        content.append("")
        
        api_dir = output_dir / 'api'
        api_dir.mkdir(exist_ok=True)
        
        api_content = []
        api_content.append("API Reference")
        api_content.append("=============")
        api_content.append("")
        api_content.append(".. toctree::")
        api_content.append("   :maxdepth: 1")
        api_content.append("")
        
        for source_file in source_files:
            if source_file.modules or source_file.functions or source_file.subroutines:
                filename = source_file.filepath.stem
                api_content.append(f"   {filename}")
        
        # Write modules index
        modules_index = modules_dir / 'index.rst'
        with open(modules_index, 'w', encoding='utf-8') as f:
            f.write('\n'.join(content))
        
        # Write API index
        api_index = api_dir / 'index.rst'
        with open(api_index, 'w', encoding='utf-8') as f:
            f.write('\n'.join(api_content))


class DoxygenGenerator(DocumentationGenerator):
    """Generate Doxygen-compatible documentation."""
    
    def __init__(self, project_name: str = "GSI/EnKF Documentation"):
        """Initialize Doxygen generator."""
        self.project_name = project_name
    
    def generate_documentation(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate Doxygen documentation from source files."""
        logger.info("Generating Doxygen documentation...")
        
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Generate Doxyfile
        self._generate_doxyfile(output_dir)
        
        # Generate documentation pages for each file
        for source_file in source_files:
            self._generate_doxygen_file(source_file, output_dir)
        
        logger.info(f"Doxygen documentation generated in {output_dir}")
        return True
    
    def generate_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate Doxygen index."""
        return self._generate_mainpage(source_files, output_dir)
    
    def _generate_doxyfile(self, output_dir: Path) -> None:
        """Generate Doxygen configuration file."""
        doxyfile_content = f'''# Doxyfile for {self.project_name}

PROJECT_NAME           = "{self.project_name}"
PROJECT_NUMBER         = "1.0.0"
PROJECT_BRIEF          = "Automatically generated documentation for GSI/EnKF Fortran code"

OUTPUT_DIRECTORY       = {output_dir}
CREATE_SUBDIRS         = NO

INPUT                  = {output_dir}
INPUT_ENCODING         = UTF-8
FILE_PATTERNS          = *.md *.dox
RECURSIVE              = YES

GENERATE_HTML          = YES
HTML_OUTPUT            = html
HTML_FILE_EXTENSION    = .html

GENERATE_LATEX         = YES
LATEX_OUTPUT           = latex

GENERATE_XML           = NO
GENERATE_RTF           = NO
GENERATE_MAN           = NO

EXTRACT_ALL            = YES
EXTRACT_PRIVATE        = NO
EXTRACT_STATIC         = YES

SOURCE_BROWSER         = YES
INLINE_SOURCES         = NO

GENERATE_TREEVIEW      = YES
USE_MATHJAX            = YES

OPTIMIZE_FOR_FORTRAN   = YES
OPTIMIZE_OUTPUT_FOR_C  = NO

CASE_SENSE_NAMES       = YES
HIDE_UNDOC_MEMBERS     = NO
HIDE_UNDOC_CLASSES     = NO

WARN_IF_UNDOCUMENTED   = YES
WARN_IF_DOC_ERROR      = YES
WARN_NO_PARAMDOC       = YES
'''
        
        doxyfile_path = output_dir / 'Doxyfile'
        with open(doxyfile_path, 'w', encoding='utf-8') as f:
            f.write(doxyfile_content)
    
    def _generate_mainpage(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate Doxygen main page."""
        mainpage_content = f'''/**
\\mainpage {self.project_name}

\\section intro_sec Introduction

Welcome to the {self.project_name}. This documentation is automatically 
generated from structured comments in Fortran source code using the GSI/EnKF
documentation generation system.

\\section modules_sec Modules

The following modules are documented:

'''
        
        for source_file in source_files:
            if source_file.modules:
                for module in source_file.modules:
                    mainpage_content += f"- \\ref {module.name}\n"
        
        mainpage_content += '''
\\section functions_sec Functions and Subroutines

The codebase contains numerous functions and subroutines organized by functional category:

- Background Grid Operations
- Core Analysis Algorithms  
- I/O Interface Management
- Observation Processing
- Utility Functions

\\section build_sec Building Documentation

This documentation can be built using:
- Sphinx for reStructuredText output
- Doxygen for HTML/LaTeX output
- Custom HTML generator for web viewing

*/
'''
        
        mainpage_path = output_dir / 'mainpage.dox'
        with open(mainpage_path, 'w', encoding='utf-8') as f:
            f.write(mainpage_content)
        
        return True
    
    def _generate_doxygen_file(self, source_file: SourceFile, output_dir: Path) -> None:
        """Generate Doxygen documentation for a single source file."""
        filename = source_file.filepath.stem
        doc_path = output_dir / f'{filename}.dox'
        
        content = []
        content.append("/**")
        content.append(f"\\file {source_file.filepath.name}")
        content.append(f"\\brief Documentation for {filename}")
        content.append("*/")
        content.append("")
        
        # Document modules
        for module in source_file.modules:
            content.extend(self._format_module_doxygen(module))
        
        # Document functions
        for function in source_file.functions:
            content.extend(self._format_function_doxygen(function))
        
        # Document subroutines
        for subroutine in source_file.subroutines:
            content.extend(self._format_subroutine_doxygen(subroutine))
        
        with open(doc_path, 'w', encoding='utf-8') as f:
            f.write('\n'.join(content))
    
    def _format_module_doxygen(self, module: Module) -> List[str]:
        """Format module documentation as Doxygen comments."""
        content = []
        content.append("/**")
        content.append(f"\\defgroup {module.name} {module.name}")
        content.append(f"\\brief {module.brief}")
        content.append("")
        if module.details:
            content.append(module.details)
            content.append("")
        content.append("*/")
        content.append("")
        return content
    
    def _format_function_doxygen(self, function: Function) -> List[str]:
        """Format function documentation as Doxygen comments."""
        content = []
        content.append("/**")
        content.append(f"\\brief {function.brief}")
        content.append("")
        if function.details:
            content.append(function.details)
            content.append("")
        
        for param in function.parameters:
            direction = f"[{param.direction}]"
            content.append(f"\\param {direction} {param.name} {param.description}")
        
        if function.return_value:
            content.append(f"\\return {function.return_value}")
        
        if function.see_also:
            content.append(f"\\see {', '.join(function.see_also)}")
        
        content.append("*/")
        content.append("")
        return content
    
    def _format_subroutine_doxygen(self, subroutine: Subroutine) -> List[str]:
        """Format subroutine documentation as Doxygen comments."""
        content = []
        content.append("/**")
        content.append(f"\\brief {subroutine.brief}")
        content.append("")
        if subroutine.details:
            content.append(subroutine.details)
            content.append("")
        
        for param in subroutine.parameters:
            direction = f"[{param.direction}]"
            content.append(f"\\param {direction} {param.name} {param.description}")
        
        if subroutine.see_also:
            content.append(f"\\see {', '.join(subroutine.see_also)}")
        
        content.append("*/")
        content.append("")
        return content


class CommentParser:
    """
    Parser for structured Fortran comments in GSI/EnKF format.
    
    Extracts structured documentation comments using @brief, @details,
    @param, @return, @usage, @algorithm, @see tags.
    """
    
    def __init__(self):
        """Initialize comment parser with regex patterns."""
        # Patterns for structured comment tags
        self.patterns = {
            'brief': re.compile(r'!>\s*@brief\s+(.+)', re.IGNORECASE),
            'details': re.compile(r'!>\s*@details\s+(.+)', re.IGNORECASE | re.MULTILINE | re.DOTALL),
            'param': re.compile(r'!>\s*@param\[(\w+)\]\s+(\w+)\s+(.+)', re.IGNORECASE),
            'return': re.compile(r'!>\s*@return\s+(.+)', re.IGNORECASE),
            'usage': re.compile(r'!>\s*@usage\s*\n((?:!>.*\n?)*)', re.IGNORECASE | re.MULTILINE),
            'algorithm': re.compile(r'!>\s*@algorithm\s*\n((?:!>.*\n?)*)', re.IGNORECASE | re.MULTILINE),
            'see': re.compile(r'!>\s*@see\s+(.+)', re.IGNORECASE),
            'author': re.compile(r'!>\s*@author\s+(.+)', re.IGNORECASE),
            'date': re.compile(r'!>\s*@date\s+(.+)', re.IGNORECASE)
        }
        
        # Patterns for Fortran code elements
        self.code_patterns = {
            'module': re.compile(r'^\s*module\s+(\w+)', re.IGNORECASE | re.MULTILINE),
            'subroutine': re.compile(r'^\s*subroutine\s+(\w+)', re.IGNORECASE | re.MULTILINE),
            'function': re.compile(r'^\s*(?:type\s*\(\s*\w+\s*\)\s+)?function\s+(\w+)', re.IGNORECASE | re.MULTILINE)
        }
    
    def parse_file(self, filepath: Path) -> SourceFile:
        """
        Parse a Fortran source file and extract documented elements.
        
        Args:
            filepath: Path to the Fortran source file
            
        Returns:
            SourceFile object containing all documented elements
        """
        logger.info(f"Parsing file: {filepath}")
        
        try:
            with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
                content = f.read()
        except Exception as e:
            logger.error(f"Error reading file {filepath}: {e}")
            return SourceFile(filepath)
        
        source_file = SourceFile(filepath)
        
        # Split content into comment blocks and code blocks
        comment_blocks = self._extract_comment_blocks(content)
        
        for block in comment_blocks:
            element = self._parse_comment_block(block)
            if element:
                if isinstance(element, Module):
                    source_file.modules.append(element)
                elif isinstance(element, Function):
                    source_file.functions.append(element)
                elif isinstance(element, Subroutine):
                    source_file.subroutines.append(element)
        
        return source_file
    
    def _extract_comment_blocks(self, content: str) -> List[str]:
        """Extract structured comment blocks from source content."""
        # Look for comment blocks that start with @brief
        blocks = []
        lines = content.split('\n')
        current_block = []
        in_block = False
        
        for line in lines:
            stripped = line.strip()
            
            # Start of a structured comment block
            if re.match(r'!>\s*@brief', stripped, re.IGNORECASE):
                if current_block:
                    blocks.append('\n'.join(current_block))
                current_block = [line]
                in_block = True
            
            # Continuation of comment block
            elif in_block and stripped.startswith('!>'):
                current_block.append(line)
            
            # End of comment block (non-comment line or different comment style)
            elif in_block and not stripped.startswith('!>'):
                # Include the next few non-comment lines to capture the function/subroutine definition
                lookahead = 0
                for next_line in lines[lines.index(line):lines.index(line)+5]:
                    if self._is_code_definition(next_line):
                        current_block.append(next_line)
                        break
                    elif next_line.strip():  # Non-empty line
                        current_block.append(next_line)
                        lookahead += 1
                        if lookahead >= 3:  # Don't look too far ahead
                            break
                
                blocks.append('\n'.join(current_block))
                current_block = []
                in_block = False
        
        if current_block:
            blocks.append('\n'.join(current_block))
        
        return blocks
    
    def _is_code_definition(self, line: str) -> bool:
        """Check if line contains a function, subroutine, or module definition."""
        stripped = line.strip().lower()
        return any(stripped.startswith(keyword) for keyword in 
                  ['subroutine', 'function', 'module', 'program'])
    
    def _parse_comment_block(self, block: str) -> Optional[Union[Module, Function, Subroutine]]:
        """Parse a single structured comment block."""
        # Extract basic information
        brief_match = self.patterns['brief'].search(block)
        if not brief_match:
            return None
        
        brief = brief_match.group(1).strip()
        
        # Extract details (may span multiple lines)
        details = self._extract_multiline_field(block, 'details')
        
        # Extract parameters
        parameters = []
        for match in self.patterns['param'].finditer(block):
            direction = match.group(1)
            name = match.group(2)
            description = match.group(3).strip()
            parameters.append(Parameter(name, direction, description))
        
        # Extract other fields
        return_value = None
        return_match = self.patterns['return'].search(block)
        if return_match:
            return_value = return_match.group(1).strip()
        
        usage = self._extract_multiline_field(block, 'usage')
        algorithm = self._extract_multiline_field(block, 'algorithm')
        
        see_also = []
        see_match = self.patterns['see'].search(block)
        if see_match:
            see_also = [item.strip() for item in see_match.group(1).split(',')]
        
        author = None
        author_match = self.patterns['author'].search(block)
        if author_match:
            author = author_match.group(1).strip()
        
        date = None
        date_match = self.patterns['date'].search(block)
        if date_match:
            date = date_match.group(1).strip()
        
        # Determine the type of code element
        element_name = self._extract_code_element_name(block)
        if not element_name:
            return None
        
        element_type = self._determine_element_type(block)
        
        if element_type == 'module':
            return Module(
                name=element_name,
                brief=brief,
                details=details,
                author=author,
                date=date,
                see_also=see_also
            )
        elif element_type == 'function':
            return Function(
                name=element_name,
                brief=brief,
                details=details,
                parameters=parameters,
                return_value=return_value,
                usage=usage,
                algorithm=algorithm,
                author=author,
                date=date,
                see_also=see_also
            )
        elif element_type == 'subroutine':
            return Subroutine(
                name=element_name,
                brief=brief,
                details=details,
                parameters=parameters,
                usage=usage,
                algorithm=algorithm,
                author=author,
                date=date,
                see_also=see_also
            )
        
        return None
    
    def _extract_multiline_field(self, block: str, field_name: str) -> Optional[str]:
        """Extract multiline field content from comment block."""
        pattern = self.patterns[field_name]
        match = pattern.search(block)
        if not match:
            return None
        
        if field_name in ['usage', 'algorithm']:
            # Extract the multiline content
            content = match.group(1)
            # Clean up the comment prefixes
            lines = content.split('\n')
            cleaned_lines = []
            for line in lines:
                cleaned = re.sub(r'^!>\s*', '', line)
                if cleaned.strip():
                    cleaned_lines.append(cleaned)
            return '\n'.join(cleaned_lines) if cleaned_lines else None
        else:
            return match.group(1).strip()
    
    def _extract_code_element_name(self, block: str) -> Optional[str]:
        """Extract the name of the code element (function, subroutine, module)."""
        for pattern in self.code_patterns.values():
            match = pattern.search(block)
            if match:
                return match.group(1)
        return None
    
    def _determine_element_type(self, block: str) -> Optional[str]:
        """Determine if the code element is a module, function, or subroutine."""
        for element_type, pattern in self.code_patterns.items():
            if pattern.search(block):
                return element_type
        return None


class HTMLGenerator(DocumentationGenerator):
    """Generate custom HTML documentation."""
    
    def __init__(self, project_name: str = "GSI/EnKF Documentation"):
        """Initialize HTML generator."""
        self.project_name = project_name
    
    def generate_documentation(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate HTML documentation from source files."""
        logger.info("Generating HTML documentation...")
        
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Generate CSS styles
        self._generate_css(output_dir)
        
        # Generate main index
        self._generate_html_index(source_files, output_dir)
        
        # Generate documentation for each source file
        for source_file in source_files:
            self._generate_html_file(source_file, output_dir)
        
        # Generate navigation menu
        self._generate_navigation(source_files, output_dir)
        
        logger.info(f"HTML documentation generated in {output_dir}")
        return True
    
    def generate_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate HTML index."""
        return self._generate_html_index(source_files, output_dir)
    
    def _generate_css(self, output_dir: Path) -> None:
        """Generate CSS stylesheet."""
        css_content = '''
/* GSI/EnKF Documentation Styles */

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    line-height: 1.6;
    color: #333;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    background-color: #f8f9fa;
}

header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 2rem;
    border-radius: 10px;
    margin-bottom: 2rem;
    text-align: center;
}

h1 {
    margin: 0;
    font-size: 2.5em;
    text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}

h2 {
    color: #2c3e50;
    border-bottom: 3px solid #3498db;
    padding-bottom: 10px;
    margin-top: 2rem;
}

h3 {
    color: #34495e;
    margin-top: 1.5rem;
}

.container {
    background: white;
    padding: 2rem;
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    margin-bottom: 2rem;
}

.function, .subroutine, .module {
    border: 1px solid #e1e8ed;
    border-radius: 8px;
    padding: 1.5rem;
    margin-bottom: 2rem;
    background: #fdfdfd;
}

.function h3, .subroutine h3, .module h3 {
    background: #3498db;
    color: white;
    padding: 0.5rem 1rem;
    margin: -1.5rem -1.5rem 1rem -1.5rem;
    border-radius: 8px 8px 0 0;
}

.parameters {
    background: #f8f9fa;
    border-left: 4px solid #28a745;
    padding: 1rem;
    margin: 1rem 0;
}

.parameter {
    margin-bottom: 0.5rem;
}

.param-name {
    font-weight: bold;
    color: #2c3e50;
}

.param-direction {
    background: #007bff;
    color: white;
    padding: 2px 6px;
    border-radius: 3px;
    font-size: 0.8em;
    margin-left: 5px;
}

.usage {
    background: #f1f3f4;
    border: 1px solid #dadce0;
    border-radius: 4px;
    padding: 1rem;
    font-family: 'Courier New', monospace;
    font-size: 0.9em;
    margin: 1rem 0;
    overflow-x: auto;
}

.algorithm {
    background: #fff3cd;
    border-left: 4px solid #ffc107;
    padding: 1rem;
    margin: 1rem 0;
}

.see-also {
    background: #d4edda;
    border-left: 4px solid #28a745;
    padding: 1rem;
    margin: 1rem 0;
}

.navigation {
    position: fixed;
    top: 20px;
    right: 20px;
    background: white;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 1rem;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    max-width: 250px;
    max-height: 80vh;
    overflow-y: auto;
}

.navigation h4 {
    margin-top: 0;
    color: #2c3e50;
}

.navigation ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.navigation li {
    margin: 5px 0;
}

.navigation a {
    text-decoration: none;
    color: #3498db;
    display: block;
    padding: 5px;
    border-radius: 3px;
    transition: background 0.2s;
}

.navigation a:hover {
    background: #f8f9fa;
    color: #2980b9;
}

@media (max-width: 768px) {
    .navigation {
        position: relative;
        top: auto;
        right: auto;
        max-width: 100%;
        margin-bottom: 2rem;
    }
    
    body {
        padding: 10px;
    }
}
'''
        
        css_path = output_dir / 'style.css'
        with open(css_path, 'w', encoding='utf-8') as f:
            f.write(css_content)
    
    def _generate_html_index(self, source_files: List[SourceFile], output_dir: Path) -> bool:
        """Generate main HTML index page."""
        content = f'''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{self.project_name}</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>{self.project_name}</h1>
        <p>Automatically generated from structured Fortran comments</p>
    </header>
    
    <div class="container">
        <h2>Overview</h2>
        <p>This documentation is automatically generated from structured comments in GSI (Gridpoint Statistical Interpolation) and EnKF (Ensemble Kalman Filter) Fortran source code.</p>
        
        <h2>Contents</h2>
        <div class="content-grid">
'''
        
        # Count elements
        module_count = sum(len(sf.modules) for sf in source_files)
        function_count = sum(len(sf.functions) for sf in source_files)
        subroutine_count = sum(len(sf.subroutines) for sf in source_files)
        
        content += f'''
            <h3>Statistics</h3>
            <ul>
                <li><strong>{len(source_files)}</strong> source files</li>
                <li><strong>{module_count}</strong> modules</li>
                <li><strong>{function_count}</strong> functions</li>
                <li><strong>{subroutine_count}</strong> subroutines</li>
            </ul>
            
            <h3>Source Files</h3>
            <ul>
'''
        
        for source_file in source_files:
            if source_file.modules or source_file.functions or source_file.subroutines:
                filename = source_file.filepath.stem
                content += f'                <li><a href="{filename}.html">{filename}</a></li>\n'
        
        content += '''
            </ul>
        </div>
    </div>
</body>
</html>
'''
        
        index_path = output_dir / 'index.html'
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        return True
    
    def _generate_html_file(self, source_file: SourceFile, output_dir: Path) -> None:
        """Generate HTML documentation for a single source file."""
        filename = source_file.filepath.stem
        doc_path = output_dir / f'{filename}.html'
        
        content = f'''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{filename} - {self.project_name}</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>{filename}</h1>
        <p>Source file: {source_file.filepath}</p>
    </header>
    
    <div class="navigation">
        <h4>Navigation</h4>
        <ul>
            <li><a href="index.html">← Back to Index</a></li>
'''
        
        # Add navigation links for elements in this file
        if source_file.modules:
            content += '<li><strong>Modules:</strong></li>'
            for module in source_file.modules:
                content += f'<li><a href="#{module.name.lower()}">{module.name}</a></li>'
        
        if source_file.functions:
            content += '<li><strong>Functions:</strong></li>'
            for function in source_file.functions:
                content += f'<li><a href="#{function.name.lower()}">{function.name}</a></li>'
        
        if source_file.subroutines:
            content += '<li><strong>Subroutines:</strong></li>'
            for subroutine in source_file.subroutines:
                content += f'<li><a href="#{subroutine.name.lower()}">{subroutine.name}</a></li>'
        
        content += '''
        </ul>
    </div>
    
    <div class="container">
'''
        
        # Document modules
        for module in source_file.modules:
            content += self._format_module_html(module)
        
        # Document functions
        for function in source_file.functions:
            content += self._format_function_html(function)
        
        # Document subroutines
        for subroutine in source_file.subroutines:
            content += self._format_subroutine_html(subroutine)
        
        content += '''
    </div>
</body>
</html>
'''
        
        with open(doc_path, 'w', encoding='utf-8') as f:
            f.write(content)
    
    def _format_module_html(self, module: Module) -> str:
        """Format module documentation as HTML."""
        content = f'''
        <div class="module" id="{module.name.lower()}">
            <h3>Module: {module.name}</h3>
            <p><strong>Brief:</strong> {module.brief}</p>
'''
        
        if module.details:
            content += f'            <p><strong>Details:</strong> {module.details}</p>\n'
        
        if module.components:
            content += '            <div class="components">\n'
            content += '                <h4>Components:</h4>\n'
            content += '                <ul>\n'
            for component in module.components:
                content += f'                    <li>{component}</li>\n'
            content += '                </ul>\n'
            content += '            </div>\n'
        
        if module.see_also:
            content += '            <div class="see-also">\n'
            content += '                <strong>See Also:</strong> ' + ', '.join(module.see_also) + '\n'
            content += '            </div>\n'
        
        content += '        </div>\n'
        return content
    
    def _format_function_html(self, function: Function) -> str:
        """Format function documentation as HTML."""
        content = f'''
        <div class="function" id="{function.name.lower()}">
            <h3>Function: {function.name}</h3>
            <p><strong>Brief:</strong> {function.brief}</p>
'''
        
        if function.details:
            content += f'            <p><strong>Details:</strong> {function.details}</p>\n'
        
        if function.parameters:
            content += '            <div class="parameters">\n'
            content += '                <h4>Parameters:</h4>\n'
            for param in function.parameters:
                direction_class = param.direction.lower()
                units = f' [{param.units}]' if param.units else ''
                content += f'                <div class="parameter">\n'
                content += f'                    <span class="param-name">{param.name}</span>\n'
                content += f'                    <span class="param-direction">{param.direction.upper()}</span>\n'
                content += f'                    {param.description}{units}\n'
                content += f'                </div>\n'
            content += '            </div>\n'
        
        if function.return_value:
            content += f'            <p><strong>Returns:</strong> {function.return_value}</p>\n'
        
        if function.usage:
            content += f'            <div class="usage">\n'
            content += f'                <h4>Usage:</h4>\n'
            content += f'                <pre>{function.usage}</pre>\n'
            content += f'            </div>\n'
        
        if function.algorithm:
            content += f'            <div class="algorithm">\n'
            content += f'                <h4>Algorithm:</h4>\n'
            content += f'                <pre>{function.algorithm}</pre>\n'
            content += f'            </div>\n'
        
        if function.see_also:
            content += '            <div class="see-also">\n'
            content += '                <strong>See Also:</strong> ' + ', '.join(function.see_also) + '\n'
            content += '            </div>\n'
        
        content += '        </div>\n'
        return content
    
    def _format_subroutine_html(self, subroutine: Subroutine) -> str:
        """Format subroutine documentation as HTML."""
        content = f'''
        <div class="subroutine" id="{subroutine.name.lower()}">
            <h3>Subroutine: {subroutine.name}</h3>
            <p><strong>Brief:</strong> {subroutine.brief}</p>
'''
        
        if subroutine.details:
            content += f'            <p><strong>Details:</strong> {subroutine.details}</p>\n'
        
        if subroutine.parameters:
            content += '            <div class="parameters">\n'
            content += '                <h4>Parameters:</h4>\n'
            for param in subroutine.parameters:
                units = f' [{param.units}]' if param.units else ''
                content += f'                <div class="parameter">\n'
                content += f'                    <span class="param-name">{param.name}</span>\n'
                content += f'                    <span class="param-direction">{param.direction.upper()}</span>\n'
                content += f'                    {param.description}{units}\n'
                content += f'                </div>\n'
            content += '            </div>\n'
        
        if subroutine.usage:
            content += f'            <div class="usage">\n'
            content += f'                <h4>Usage:</h4>\n'
            content += f'                <pre>{subroutine.usage}</pre>\n'
            content += f'            </div>\n'
        
        if subroutine.algorithm:
            content += f'            <div class="algorithm">\n'
            content += f'                <h4>Algorithm:</h4>\n'
            content += f'                <pre>{subroutine.algorithm}</pre>\n'
            content += f'            </div>\n'
        
        if subroutine.see_also:
            content += '            <div class="see-also">\n'
            content += '                <strong>See Already:</strong> ' + ', '.join(subroutine.see_also) + '\n'
            content += '            </div>\n'
        
        content += '        </div>\n'
        return content
    
    def _generate_navigation(self, source_files: List[SourceFile], output_dir: Path) -> None:
        """Generate navigation data for client-side navigation."""
        nav_data = {
            'files': [],
            'modules': [],
            'functions': [],
            'subroutines': []
        }
        
        for source_file in source_files:
            if source_file.modules or source_file.functions or source_file.subroutines:
                filename = source_file.filepath.stem
                nav_data['files'].append({
                    'name': filename,
                    'url': f'{filename}.html'
                })
                
                for module in source_file.modules:
                    nav_data['modules'].append({
                        'name': module.name,
                        'file': filename,
                        'url': f'{filename}.html#{module.name.lower()}'
                    })
                
                for function in source_file.functions:
                    nav_data['functions'].append({
                        'name': function.name,
                        'file': filename,
                        'url': f'{filename}.html#{function.name.lower()}'
                    })
                
                for subroutine in source_file.subroutines:
                    nav_data['subroutines'].append({
                        'name': subroutine.name,
                        'file': filename,
                        'url': f'{filename}.html#{subroutine.name.lower()}'
                    })
        
        nav_path = output_dir / 'navigation.json'
        with open(nav_path, 'w', encoding='utf-8') as f:
            json.dump(nav_data, f, indent=2)


class CodeRelationshipAnalyzer:
    """Analyze relationships between code elements for visualization."""
    
    def __init__(self):
        """Initialize relationship analyzer."""
        self.relationships = []
        self.call_graph = {}
        self.dependency_graph = {}
    
    def analyze_relationships(self, source_files: List[SourceFile]) -> Dict[str, Any]:
        """
        Analyze relationships between code elements.
        
        Args:
            source_files: List of parsed source files
            
        Returns:
            Dictionary containing relationship analysis results
        """
        logger.info("Analyzing code relationships...")
        
        # Build cross-reference maps
        all_elements = self._build_element_maps(source_files)
        
        # Analyze see_also relationships
        see_also_relationships = self._analyze_see_also_relationships(source_files)
        
        # Analyze parameter type relationships  
        parameter_relationships = self._analyze_parameter_relationships(source_files)
        
        # Create visualization data
        visualization_data = self._create_visualization_data(
            all_elements, see_also_relationships, parameter_relationships
        )
        
        return {
            'elements': all_elements,
            'see_also_relationships': see_also_relationships,
            'parameter_relationships': parameter_relationships,
            'visualization_data': visualization_data
        }
    
    def _build_element_maps(self, source_files: List[SourceFile]) -> Dict[str, Dict]:
        """Build maps of all code elements."""
        elements = {
            'modules': {},
            'functions': {},
            'subroutines': {}
        }
        
        for source_file in source_files:
            filename = str(source_file.filepath)
            
            for module in source_file.modules:
                elements['modules'][module.name] = {
                    'file': filename,
                    'brief': module.brief,
                    'details': module.details
                }
            
            for function in source_file.functions:
                elements['functions'][function.name] = {
                    'file': filename,
                    'brief': function.brief,
                    'details': function.details,
                    'parameters': [p.name for p in function.parameters],
                    'return_value': function.return_value
                }
            
            for subroutine in source_file.subroutines:
                elements['subroutines'][subroutine.name] = {
                    'file': filename,
                    'brief': subroutine.brief,
                    'details': subroutine.details,
                    'parameters': [p.name for p in subroutine.parameters]
                }
        
        return elements
    
    def _analyze_see_also_relationships(self, source_files: List[SourceFile]) -> List[Dict]:
        """Analyze see_also cross-references."""
        relationships = []
        
        for source_file in source_files:
            for element in (source_file.modules + source_file.functions + source_file.subroutines):
                if element.see_also:
                    for ref in element.see_also:
                        relationships.append({
                            'from': element.name,
                            'to': ref.strip(),
                            'type': 'see_also',
                            'source_file': str(source_file.filepath)
                        })
        
        return relationships
    
    def _analyze_parameter_relationships(self, source_files: List[SourceFile]) -> List[Dict]:
        """Analyze parameter type relationships."""
        relationships = []
        
        for source_file in source_files:
            for element in (source_file.functions + source_file.subroutines):
                for param in element.parameters:
                    # Look for derived type parameters that might reference modules
                    if 'type(' in param.description.lower():
                        # Extract potential type name
                        type_match = re.search(r'type\s*\(\s*(\w+)\s*\)', param.description, re.IGNORECASE)
                        if type_match:
                            type_name = type_match.group(1)
                            relationships.append({
                                'from': element.name,
                                'to': type_name,
                                'type': 'uses_type',
                                'parameter': param.name,
                                'source_file': str(source_file.filepath)
                            })
        
        return relationships
    
    def _create_visualization_data(self, elements: Dict, see_also: List[Dict], 
                                  parameters: List[Dict]) -> Dict[str, Any]:
        """Create data suitable for network visualization."""
        nodes = []
        edges = []
        
        # Add nodes for all elements
        for category, items in elements.items():
            for name, info in items.items():
                nodes.append({
                    'id': name,
                    'label': name,
                    'category': category.rstrip('s'),  # Remove 's' for singular
                    'file': info['file'],
                    'brief': info['brief']
                })
        
        # Add edges for relationships
        edge_id = 0
        for rel in see_also + parameters:
            edges.append({
                'id': edge_id,
                'from': rel['from'],
                'to': rel['to'],
                'type': rel['type'],
                'label': rel['type'].replace('_', ' ').title()
            })
            edge_id += 1
        
        return {
            'nodes': nodes,
            'edges': edges,
            'statistics': {
                'total_nodes': len(nodes),
                'total_edges': len(edges),
                'modules': len(elements['modules']),
                'functions': len(elements['functions']),
                'subroutines': len(elements['subroutines'])
            }
        }


class DocumentationManager:
    """
    Main manager for automated documentation generation.
    
    Coordinates parsing, analysis, and generation across multiple formats.
    """
    
    def __init__(self, project_name: str = "GSI/EnKF Documentation"):
        """Initialize documentation manager."""
        self.project_name = project_name
        self.parser = CommentParser()
        self.relationship_analyzer = CodeRelationshipAnalyzer()
        
        # Available generators
        self.generators = {
            'sphinx': SphinxGenerator(project_name),
            'doxygen': DoxygenGenerator(project_name),
            'html': HTMLGenerator(project_name)
        }
    
    def generate_documentation(self, 
                             source_paths: List[Union[str, Path]],
                             output_dir: Union[str, Path],
                             formats: List[str] = None,
                             analyze_relationships: bool = True) -> Dict[str, Any]:
        """
        Generate documentation from Fortran source files.
        
        Args:
            source_paths: List of source file or directory paths
            output_dir: Output directory for generated documentation
            formats: List of output formats ('sphinx', 'doxygen', 'html')
            analyze_relationships: Whether to analyze code relationships
            
        Returns:
            Dictionary containing generation results and statistics
        """
        if formats is None:
            formats = ['html']
        
        # Validate formats
        invalid_formats = [f for f in formats if f not in self.generators]
        if invalid_formats:
            raise ValueError(f"Invalid formats: {invalid_formats}. "
                           f"Available: {list(self.generators.keys())}")
        
        output_dir = Path(output_dir)
        logger.info(f"Starting documentation generation to {output_dir}")
        
        # Discover source files
        source_files = self._discover_source_files(source_paths)
        logger.info(f"Found {len(source_files)} source files to process")
        
        # Parse all source files
        parsed_files = []
        for source_file in source_files:
            try:
                parsed = self.parser.parse_file(source_file)
                if parsed.modules or parsed.functions or parsed.subroutines:
                    parsed_files.append(parsed)
                    logger.info(f"Parsed {source_file}: {len(parsed.modules)} modules, "
                              f"{len(parsed.functions)} functions, {len(parsed.subroutines)} subroutines")
            except Exception as e:
                logger.error(f"Error parsing {source_file}: {e}")
        
        logger.info(f"Successfully parsed {len(parsed_files)} files with documentation")
        
        # Analyze relationships if requested
        relationship_data = None
        if analyze_relationships and parsed_files:
            try:
                relationship_data = self.relationship_analyzer.analyze_relationships(parsed_files)
                logger.info(f"Analyzed relationships: {len(relationship_data['see_also_relationships'])} "
                          f"see-also, {len(relationship_data['parameter_relationships'])} parameter")
            except Exception as e:
                logger.error(f"Error analyzing relationships: {e}")
        
        # Generate documentation in requested formats
        generation_results = {}
        for format_name in formats:
            format_output_dir = output_dir / format_name
            try:
                generator = self.generators[format_name]
                success = generator.generate_documentation(parsed_files, format_output_dir)
                generation_results[format_name] = {
                    'success': success,
                    'output_dir': str(format_output_dir),
                    'files_count': len(parsed_files)
                }
                logger.info(f"Generated {format_name} documentation: {success}")
            except Exception as e:
                logger.error(f"Error generating {format_name} documentation: {e}")
                generation_results[format_name] = {
                    'success': False,
                    'error': str(e),
                    'output_dir': str(format_output_dir)
                }
        
        # Save relationship data if available
        if relationship_data:
            self._save_relationship_data(relationship_data, output_dir)
        
        # Generate summary report
        summary = self._generate_summary_report(parsed_files, generation_results, relationship_data)
        
        # Save summary report
        summary_path = output_dir / 'generation_summary.json'
        with open(summary_path, 'w', encoding='utf-8') as f:
            json.dump(summary, f, indent=2)
        
        logger.info(f"Documentation generation completed. Summary saved to {summary_path}")
        
        return {
            'summary': summary,
            'parsed_files': len(parsed_files),
            'generation_results': generation_results,
            'relationship_data': relationship_data
        }
    
    def _discover_source_files(self, source_paths: List[Union[str, Path]]) -> List[Path]:
        """Discover Fortran source files from given paths."""
        source_files = []
        fortran_extensions = {'.f90', '.f', '.f95', '.f03', '.f08', '.F90', '.F', '.F95', '.F03', '.F08'}
        
        for path in source_paths:
            path = Path(path)
            
            if path.is_file():
                if path.suffix in fortran_extensions:
                    source_files.append(path)
            elif path.is_dir():
                # Recursively find Fortran files
                for ext in fortran_extensions:
                    source_files.extend(path.rglob(f'*{ext}'))
            else:
                logger.warning(f"Path not found: {path}")
        
        # Remove duplicates and sort
        source_files = sorted(set(source_files))
        return source_files
    
    def _save_relationship_data(self, relationship_data: Dict[str, Any], output_dir: Path) -> None:
        """Save relationship analysis data."""
        relationships_dir = output_dir / 'relationships'
        relationships_dir.mkdir(exist_ok=True)
        
        # Save visualization data for web-based viewers
        viz_path = relationships_dir / 'visualization_data.json'
        with open(viz_path, 'w', encoding='utf-8') as f:
            json.dump(relationship_data['visualization_data'], f, indent=2)
        
        # Save detailed relationship data
        details_path = relationships_dir / 'relationship_details.json'
        with open(details_path, 'w', encoding='utf-8') as f:
            json.dump({
                'see_also_relationships': relationship_data['see_also_relationships'],
                'parameter_relationships': relationship_data['parameter_relationships'],
                'elements': relationship_data['elements']
            }, f, indent=2)
        
        logger.info(f"Relationship data saved to {relationships_dir}")
    
    def _generate_summary_report(self, 
                                parsed_files: List[SourceFile],
                                generation_results: Dict[str, Dict],
                                relationship_data: Optional[Dict[str, Any]]) -> Dict[str, Any]:
        """Generate comprehensive summary report."""
        # Count elements
        total_modules = sum(len(f.modules) for f in parsed_files)
        total_functions = sum(len(f.functions) for f in parsed_files)
        total_subroutines = sum(len(f.subroutines) for f in parsed_files)
        
        # File statistics
        files_with_modules = len([f for f in parsed_files if f.modules])
        files_with_functions = len([f for f in parsed_files if f.functions])
        files_with_subroutines = len([f for f in parsed_files if f.subroutines])
        
        # Generation statistics
        successful_formats = [fmt for fmt, result in generation_results.items() 
                            if result.get('success', False)]
        failed_formats = [fmt for fmt, result in generation_results.items() 
                        if not result.get('success', False)]
        
        summary = {
            'timestamp': subprocess.run(['date', '-Iseconds'], 
                                      capture_output=True, text=True).stdout.strip(),
            'project_name': self.project_name,
            'parsing_statistics': {
                'total_files_processed': len(parsed_files),
                'total_modules': total_modules,
                'total_functions': total_functions,
                'total_subroutines': total_subroutines,
                'files_with_modules': files_with_modules,
                'files_with_functions': files_with_functions,
                'files_with_subroutines': files_with_subroutines
            },
            'generation_results': generation_results,
            'successful_formats': successful_formats,
            'failed_formats': failed_formats
        }
        
        if relationship_data:
            viz_stats = relationship_data['visualization_data']['statistics']
            summary['relationship_analysis'] = {
                'total_nodes': viz_stats['total_nodes'],
                'total_edges': viz_stats['total_edges'],
                'see_also_relationships': len(relationship_data['see_also_relationships']),
                'parameter_relationships': len(relationship_data['parameter_relationships'])
            }
        
        return summary


def main():
    """Main entry point for documentation generation."""
    parser = argparse.ArgumentParser(
        description='Generate documentation from GSI/EnKF Fortran code with structured comments'
    )
    
    parser.add_argument('sources', nargs='+', 
                       help='Source files or directories to process')
    parser.add_argument('-o', '--output', required=True,
                       help='Output directory for generated documentation')
    parser.add_argument('-f', '--formats', nargs='+', 
                       choices=['sphinx', 'doxygen', 'html'], 
                       default=['html'],
                       help='Documentation formats to generate')
    parser.add_argument('-p', '--project-name', 
                       default='GSI/EnKF Documentation',
                       help='Project name for documentation')
    parser.add_argument('--no-relationships', action='store_true',
                       help='Skip relationship analysis')
    parser.add_argument('-v', '--verbose', action='store_true',
                       help='Enable verbose logging')
    parser.add_argument('--build-sphinx', action='store_true',
                       help='Build Sphinx documentation after generation')
    parser.add_argument('--build-doxygen', action='store_true',
                       help='Build Doxygen documentation after generation')
    
    args = parser.parse_args()
    
    # Configure logging
    if args.verbose:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)
    
    try:
        # Initialize documentation manager
        doc_manager = DocumentationManager(args.project_name)
        
        # Generate documentation
        results = doc_manager.generate_documentation(
            source_paths=args.sources,
            output_dir=args.output,
            formats=args.formats,
            analyze_relationships=not args.no_relationships
        )
        
        # Build documentation if requested
        if args.build_sphinx and 'sphinx' in args.formats:
            build_sphinx_docs(Path(args.output) / 'sphinx')
        
        if args.build_doxygen and 'doxygen' in args.formats:
            build_doxygen_docs(Path(args.output) / 'doxygen')
        
        # Print summary
        print("\nDocumentation Generation Summary:")
        print(f"Processed {results['parsed_files']} source files")
        print(f"Generated formats: {list(results['generation_results'].keys())}")
        
        if results['relationship_data']:
            viz_stats = results['relationship_data']['visualization_data']['statistics']
            print(f"Analyzed {viz_stats['total_nodes']} code elements with {viz_stats['total_edges']} relationships")
        
        print(f"Output directory: {args.output}")
        
    except Exception as e:
        logger.error(f"Documentation generation failed: {e}")
        sys.exit(1)


def build_sphinx_docs(sphinx_dir: Path) -> bool:
    """Build Sphinx documentation using sphinx-build."""
    try:
        build_dir = sphinx_dir / '_build'
        build_dir.mkdir(exist_ok=True)
        
        cmd = [
            'sphinx-build', '-b', 'html',
            str(sphinx_dir),
            str(build_dir / 'html')
        ]
        
        logger.info(f"Building Sphinx documentation: {' '.join(cmd)}")
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
        
        if result.returncode == 0:
            logger.info("Sphinx documentation built successfully")
            return True
        else:
            logger.error(f"Sphinx build failed: {result.stderr}")
            return False
            
    except Exception as e:
        logger.error(f"Error building Sphinx documentation: {e}")
        return False


def build_doxygen_docs(doxygen_dir: Path) -> bool:
    """Build Doxygen documentation."""
    try:
        doxyfile = doxygen_dir / 'Doxyfile'
        
        if not doxyfile.exists():
            logger.error(f"Doxyfile not found: {doxyfile}")
            return False
        
        cmd = ['doxygen', str(doxyfile)]
        
        logger.info(f"Building Doxygen documentation: {' '.join(cmd)}")
        result = subprocess.run(cmd, capture_output=True, text=True, 
                              timeout=300, cwd=doxygen_dir)
        
        if result.returncode == 0:
            logger.info("Doxygen documentation built successfully")
            return True
        else:
            logger.error(f"Doxygen build failed: {result.stderr}")
            return False
            
    except Exception as e:
        logger.error(f"Error building Doxygen documentation: {e}")
        return False


if __name__ == '__main__':
    main()